package bancosys.tec.security.impl.domain;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;

import jmine.tec.persist.annotation.Constraint;
import jmine.tec.persist.annotation.DiscriminatorComment;

import org.apache.commons.lang.builder.HashCodeBuilder;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import org.hibernate.util.EqualsHelper;
import org.hibernate.validator.NotNull;

import bancosys.tec.component.Documentation;
import bancosys.tec.persist.authorization.annotation.DisplayName;
import bancosys.tec.security.authorization.ChineseWallPermission;
import bancosys.tec.security.authorization.Permission;

/**
 * Credencial para chinese wall.
 * 
 * @author Marco
 * @author Piercio
 */
@Entity
@DiscriminatorValue("2")
@DiscriminatorComment("CHINESE WALL")
@DisplayName("Credencial Chinese Wall")
public class ChineseWallCredential extends Credential {

    // tamanho máximo para a descrição
    private static final int MAX_DESC_SIZE = 256;

    // numero impar qquer
    private static final int HASH_RANDOM_1 = 25;

    // numero impar diferente do primeiro qquer
    private static final int HASH_RANDOM_2 = 49;

    /**
     * Nome que identifica a entidade onde o filtro de chinese wall é utilizado.
     */
    private String entityName;

    /**
     * Conjunto de ids da entidade que são visíveis a quem possui essa permissão.
     */
    private Set<ChineseWallEntityId> ids = new HashSet<ChineseWallEntityId>();

    /**
     * Caso true, filtro deve ser desabilitado para garantir acesso ilimitado.
     */
    private Boolean fullAccess;

    /**
     * Descrição dessa permissão
     */
    private String description;

    /**
     * Utilizado pelo Hibernate.
     */
    ChineseWallCredential() {
        // não faz nada
    }

    /**
     * Constrói a permissão.
     * 
     * @param entityName nome da entidade.
     * @param ids conjunto de ids.
     */
    public ChineseWallCredential(String entityName, Set<ChineseWallEntityId> ids) {
        if (entityName == null) {
            throw new IllegalArgumentException("Nome da entidade deve ser informado.");
        }
        this.entityName = entityName;
        this.ids.addAll(ids);
    }

    /**
     * Retorna o nome da entidade
     * 
     * @return nome da entidade.
     */
    @Column(name = "ENTITY_NAME", length = 255)
    @Documentation("NOME DA ENTIDADE A QUAL SERA ATRIBUIDA PERMISSAO DE ACESSO (CHINESE WALL).")
    public String getEntityName() {
        return this.entityName;
    }

    /**
     * Define o nome da entidade.
     * 
     * @param entityName nome da entidade.
     */
    public void setEntityName(String entityName) {
        this.entityName = entityName;
    }

    /**
     * Retorna o conjunto de IDs acessíveis da entidade.
     * 
     * @return o conjunto de IDs acessíveis da entidade.
     */
    @OneToMany(fetch = FetchType.LAZY)
    @JoinColumn(name = "COD_CREDENTIAL")
    @Cascade({ CascadeType.ALL, CascadeType.DELETE_ORPHAN })
    @OrderBy("entityId")
    @DisplayName(value = "IDs", order = "13")
    public Set<ChineseWallEntityId> getIds() {
        return this.ids;
    }

    /**
     * Define o conjunto de IDs.
     * 
     * @param ids o conjunto de IDs acessíveis da entidade.
     */
    void setIds(Set<ChineseWallEntityId> ids) {
        this.ids = ids;
    }

    /**
     * Devolve a descrição dessa permissão.
     * 
     * @return a descrição dessa permissão.
     */
    @Column(name = "CHINESE_WALL_DESCR", length = MAX_DESC_SIZE)
    @Documentation("DESCRICAO DA PERMISSAO PARA A CREDENCIAL DE CHINESE WALL.")
    @DisplayName(value = "Descrição", order = "11")
    public String getDescription() {
        return this.description;
    }

    /**
     * Define a descrição dessa permissão.
     * 
     * @param description a descrição dessa permissão.
     */
    public void setDescription(String description) {
        this.description = description;
    }

    /**
     * @return the fullAccess
     */
    @NotNull
    @Column(name = "FULL_ACCESS")
    @Documentation("INDICA SE A CREDENCIAL POSSUI ACESSO TOTAL AO SISTEMA (1) OU NAO (0).")
    @Constraint(name = "CK_CREDEN_1", condition = "FULL_ACCESS IN ( 0, 1 )")
    public Boolean getFullAccess() {
        return this.fullAccess;
    }

    /**
     * @param fullAccess the fullAccess to set
     */
    public void setFullAccess(Boolean fullAccess) {
        this.fullAccess = fullAccess;
    }

    /**
     * Compara esse objeto com o passado como parâmetro. Utiliza nome da entidade como critério de comparação.
     * 
     * @param obj o outro objeto.
     * @return <code>true</code> se forem iguais, <code>false</code> caso contrário.
     */
    @Override
    public boolean equals(Object obj) {
        if (!super.equals(obj)) {
            return false;
        }

        if (!(obj instanceof ChineseWallCredential)) {
            return false;
        }
        ChineseWallCredential other = (ChineseWallCredential) obj;

        if (!EqualsHelper.equals(this.entityName, other.entityName)) {
            return false;
        }

        if (!EqualsHelper.equals(this.getClient(), other.getClient())) {
            return false;
        }

        if (!EqualsHelper.equals(this.getFullAccess(), other.getFullAccess())) {
            return false;
        }
        return true;
    }

    /**
     * Devolve o hashCode.
     * 
     * @return o hashCode.
     */
    @Override
    public int hashCode() {
        int superHash = super.hashCode();
        HashCodeBuilder hashCodeBuilder = new HashCodeBuilder(HASH_RANDOM_1, HASH_RANDOM_2);
        hashCodeBuilder.appendSuper(superHash);
        hashCodeBuilder.append(this.getClient().getName());
        hashCodeBuilder.append(this.entityName);
        hashCodeBuilder.append(this.fullAccess);
        return hashCodeBuilder.toHashCode();
    }

    /**
     * Devolve a representação desse objeto.
     * 
     * @return a representação desse objeto.
     */
    @Override
    public String toString() {
        return "ChineseWallCredential (" + this.entityName + ") - " + this.description;
    }

    /**
     * Cria uma {@link Permission} de {@link ChineseWallPermission}
     * 
     * @return {@link Permission}
     */
    @Override
    public Permission createPermission() {
        if (this.getFullAccess()) {
            return ChineseWallPermission.createFullAccess(this.getMnemonico(), this.getEntityName());
        }
        Set<Long> theIds = new HashSet<Long>();
        for (ChineseWallEntityId id : this.getIds()) {
            theIds.add(id.getEntityId());
        }
        return new ChineseWallPermission(this.getMnemonico(), this.getEntityName(), theIds, this.getId());
    }
}
